/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef TERRAIN_H
#define TERRAIN_H

#include <ode/ode.h>
#include <math.h>
#include <GL/gl.h>
#include "Object.h"
#include "Primitive.h"
#include "Shapes.h"
#include "Wall.h"
#define GIMI_CLIENT_API 20000
#include "gimi.h"
#undef GIMI_CLIENT_API

#include "MapClient.hpp"

// LineMapServer stuff
#include "MapServer.hpp"
#include "lnmline.h" ///< From LnMlib for reading and handling of lines

//use the terrain displaylist class used in avant model (in GIM/src/grid)
#include "CGLHeightMap.h"

//defines the hight of the "pedestal" the terrain stands on (just a silly effect ;)
#define PEDESTAL_HEIGHT 400

#define TERRAIN_TYPE_HMAP 1
#define TERRAIN_TYPE_PLANE 2
//#define TERRAIN_TYPE_DYNAMIC 3

//ground model is divided in blocks, defines block size in slices between samples
#define GROUND_MODEL_BLOCK_SIZE 20
//2 triangles per heightmap point and 3 vertices per triangle
//take 1 row of vertices from next block to fill gaps between blocks
#define BLOCK_INDEX_COUNT ((GROUND_MODEL_BLOCK_SIZE)*(GROUND_MODEL_BLOCK_SIZE)*2*3) 
#define BLOCK_VERTEX_COUNT ((GROUND_MODEL_BLOCK_SIZE+1)*(GROUND_MODEL_BLOCK_SIZE+1))

struct TMaterial;

struct TGroundModelBlock
{
  float x, y; //position in world coordinates
  float resolution;
  int indexData[BLOCK_INDEX_COUNT];
  float vertexData[BLOCK_VERTEX_COUNT*3];
  CTrimesh* Trimesh;
  //CGLHeightMap* GLHeightMap;
  
  TGroundModelBlock(float x, float y, float resolution);
  
  ~TGroundModelBlock();
  
};
/*
typedef std::vector<TGroundModelBlock*> gmbVector;
typedef std::vector<gmbVector> gmbTable;
*/
struct TGmbKey{
  int x, y;
  
  TGmbKey(int x, int y)
  :x(x),
  y(y)
  {}
  
  bool operator == (const TGmbKey &k) const
  {
    //dPrint(1,"oper == start");
    if (x==k.x && y==k.y)
    {
      //dPrint(1,"oper == end (true)");
      return true;
    }
    //dPrint(1,"oper == end (false)");
    return false;
  }
  
  bool operator < (const TGmbKey &k) const
  {
    //dPrint(1,"oper < start");
    if (x < k.x)
    {
      //dPrint(1,"oper < end (true)");
      return true;
    } else if (x == k.x) {
      if (y < k.y)
      {
        //dPrint(1,"oper < end (true)");
        return true;
      }
    }
    //dPrint(1,"oper < end (false)");
    return false;
  }
  
};

typedef std::map<TGmbKey, TGroundModelBlock*> gmbMap;
typedef std::pair<TGmbKey, TGroundModelBlock*> gmbPair;
typedef std::map<TGmbKey, TGroundModelBlock*>::iterator gmbMapIter;


class CTerrain : public CObject
{
  public:
    //terrain from file (height map)
    CTerrain(CWorld *World, char *fileName, std::string name, bool continuous=false);
    //plane terrain
    CTerrain(CWorld *World, float x1, float x2, float y1, float y2); //, dReal a, dReal b, dReal c, dReal d);

    ~CTerrain();

    void Activate();
    void Draw();
    void Update(float StepSize);
    void OnCollision(CPrimitive *primitive, int numc, dContact *contacts, CPrimitive *primitive2);

    //static int TrimeshCallback(dGeomID trimesh, dGeomID refObject, int index);
    
    //for testing if a primitive is part of ground
    bool IsTerrain(CPrimitive *primitive);
    
    float GetHeightMapResolution(){return reso;}
    
    void getGroundModel(const float aPosX, const float aPosY, 
                                 const int data_samples_x, const int data_samples_y,
                                 float* aHmData);
    
    void updateTerrain(const float aPosX, const float aPosY, 
        const int data_samples_x, const int data_samples_y,
        float* aHmData);
    
  private:
    
    struct TUpdate
    {
      float x,y;
      int samples_x,samples_y;
      float* data;
      
    };
    
    std::string name;
    
    int type;

    bool wrap;

    gimi::GIMI gimi;
    
    /*    gimi::GIMI iLineMapGimi;
    MaCI::MaCICtrl::CMaCICtrlServer iLineMapMSC;
    MaCI::Map::CMapServer iLineMapServer;*/

 
#if ! defined(NO_MAP)
    MaCI::Map::CMapClient mapClient;
    int seq;
#endif

    //dimensions and resolution
    float width, depth;
    //float originW, originD;
    float reso;
    //int wSamples, dSamples;
    float maxHeight,minHeight;

    float x1,x2,y1,y2;

    float hmResolution;
    
    gmbMap groundBlocks;
    
    CTexturedPlane *Plane;

    TMaterial *material;

    //this is a quick solution for adding walls to world
    //TODO: something more elegant
    std::vector<CWall*> addedWalls;
    
    float updateCounter; //for testing
    float testData[21*21];
    
    std::vector<TUpdate> updates;
    
    void loadFile(char* fname);
    void drawGrid();
    
    //update (or create) part of ground model
    void updateGroundModel(const float aPosX, const float aPosY, 
                           const int data_samples_x, const int data_samples_y,
                           //const float aWidth, const float aDepth, 
                           const float aReso, const float* aHmData);
    
#ifndef NO_MAP
    void updateMap(MaCI::Map::CMapData &data);
#endif
    
    void StartLineMapServer(char* aMapName);

    void connectGIMI();

    TGroundModelBlock* getGroundBlock(int x, int y);
    void setGroundBlock(int x, int y, TGroundModelBlock *block);
    
    void clearBlocks();
    
    void clearAddedWalls();
    
};

#endif //TERRAIN_H
